package com.gitee.xmhzzz.component.tcp.server.context;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.gitee.xmhzzz.component.tcp.server.api.CacheApi;
import com.gitee.xmhzzz.component.tcp.server.config.NettyServerProperties;
import com.gitee.xmhzzz.component.tcp.server.dto.RegisteredAddressInfo;
import io.netty.channel.ChannelHandlerContext;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.time.LocalDateTime;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;


/**
 * netty客户端集合
 */
@Slf4j
public class ChannelContext {

    private static NettyServerProperties properties;

    private static CacheApi cacheApi;

    private static String IP_ADDRESS;

    public static void setProperties(NettyServerProperties properties) {
        ChannelContext.properties = properties;
    }

    public static void setCacheApi(CacheApi cacheApi) {
        ChannelContext.cacheApi = cacheApi;
    }

    static {
        try {
            IP_ADDRESS = getInnerIp();
        } catch (SocketException e) {
            log.error("获取本服务节点的IP信息出错, {}. ", e.getMessage(), e);
        }
    }

    public static String getInnerIp() throws SocketException {
        String localip = null;// 本地IP，如果没有配置外网IP则返回它
        String netip = null;// 外网IP
        Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
        InetAddress ip = null;
        boolean finded = false;// 是否找到外网IP
        while (netInterfaces.hasMoreElements() && !finded) {
            NetworkInterface ni = netInterfaces.nextElement();
            Enumeration<InetAddress> address = ni.getInetAddresses();
            while (address.hasMoreElements()) {
                ip = address.nextElement();
                if (!ip.isSiteLocalAddress()
                        && !ip.isLoopbackAddress()
                        && ip.getHostAddress().indexOf(":") == -1) {// 外网IP
                    netip = ip.getHostAddress();
                    finded = true;
                    break;
                } else if (ip.isSiteLocalAddress()
                        && !ip.isLoopbackAddress()
                        && ip.getHostAddress().indexOf(":") == -1) {// 内网IP
                    localip = ip.getHostAddress();
                }
            }
        }
        if (netip != null && !"".equals(netip)) {
            return netip;
        } else {
            return localip;
        }
    }

    private static Map<String, ChannelInfo> clientMap = new ConcurrentHashMap<>();


    public static Map<String, ChannelInfo> getClientMap() {
        return clientMap;
    }


    private static String clientKey = "client:device:";
    private static String addressKey = "address:device";

    /**
     * 设备通信：与登录的clientId对比，一致则更新最后通信时间，不一致则断开链路
     */
    public static boolean addConnect(ChannelHandlerContext ctx) {
        String clientId = ctx.channel().id().toString();
        if (clientMap.containsKey(clientId)) {
            ChannelInfo client = getClient(clientId);
            client.setLastUpdated(LocalDateTime.now());
            clientMap.put(clientId, client);
            String deviceCode = client.getDeviceCode();

            cacheApi.setString(clientKey + deviceCode, clientId, 90, TimeUnit.SECONDS);
            RegisteredAddressInfo addressInfo = new RegisteredAddressInfo()
                    .setIpAddress(IP_ADDRESS)
                    .setPort(properties.getPort());
            cacheApi.setHash(addressKey, deviceCode, JSONObject.toJSONString(addressInfo));
            if (log.isDebugEnabled()) {
                log.debug("添加设备路由信息 key[{}] deviceCode[{}] addressInfo[{}]",
                        addressKey, deviceCode, addressInfo.toString());
            }
            return true;
        } else {
            log.info("通道异常关闭ClientChanelMap.addConnect没登录信息. channelId[{}]", clientId);
            ctx.channel().close();
            ctx.close();
            return false;
        }
    }


    /**
     * 保存在线设备信息
     */
    public static void addClient(ChannelInfo client) {
        String clientId = client.getChannel().channel().id().toString();
        String oldClientId = cacheApi.getString(clientKey + client.getDeviceCode());
        if (oldClientId != null && !clientId.equals(oldClientId)) {
            // 之前设备已有通信，但是断开连接后clientId变了，关闭之前的连接
            removeClient(oldClientId);
        }
        cacheApi.setString(clientKey + client.getDeviceCode(), clientId, 90, TimeUnit.SECONDS);
        RegisteredAddressInfo addressInfo = new RegisteredAddressInfo()
                .setIpAddress(IP_ADDRESS)
                .setPort(properties.getPort());
        String deviceCode = client.getDeviceCode();
        cacheApi.setHash(addressKey, deviceCode, JSONObject.toJSONString(addressInfo));
        if (log.isDebugEnabled()) {
            log.debug("添加设备路由信息 key[{}] deviceCode[{}] addressInfo[{}]",
                    addressKey, deviceCode, addressInfo.toString());
        }
        client.setLastUpdated(LocalDateTime.now());
        clientMap.put(clientId, client);
    }


    public static ChannelInfo getClient(String clientId) {
        return clientMap.get(clientId);
    }


    public static ChannelInfo getOnlineClient(String deviceCode) {
        String clientId = cacheApi.getString(clientKey + deviceCode);
        if (clientId != null) {
            return clientMap.get(clientId);
        }
        return null;
    }

    public static RegisteredAddressInfo getServiceInfo(String deviceCode) {

        String str = cacheApi.getHashString(addressKey, deviceCode);
        if (StrUtil.isNotBlank(str)) {
            return JSONObject.parseObject(str, RegisteredAddressInfo.class);
        }
        return null;
    }


    public static void removeClient(String clientId) {
        try {
            ChannelInfo client = getClient(clientId);
            clientMap.remove(clientId);
            if (client != null) {
                log.info("通道关闭ChannelMap.removeClient channelId[{}],deviceCode[{}]", clientId, client.getDeviceCode());
                client.getChannel().channel().close();
                client.getChannel().close();
                cacheApi.delete(clientKey + client.getDeviceCode());
                // 清空设备路由信息
                String deviceCode = client.getDeviceCode();
                cacheApi.setHash(addressKey, deviceCode, JSONObject.toJSONString(new RegisteredAddressInfo()));
                if (log.isDebugEnabled()) {
                    log.debug("添加设备路由信息 key[{}] deviceCode[{}] ", addressKey, deviceCode);
                }
            }
        } catch (Exception e) {
            log.error("移除Client异常 exception[{}]", e.getMessage(), e);
        }
    }

    @Data
    public static class ChannelInfo {
        /**
         * SDK版本号
         */
        private String version;


        /**
         * 设备唯一码
         */
        private String deviceCode;


        /**
         * 最后与服务器通信时间
         */
        private LocalDateTime lastUpdated;


        /**
         * 通道对象
         */
        private ChannelHandlerContext channel;

        private String appSecret;

        private String appId;

    }
}
